package com.zenithmind.common.security;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * 动态安全过滤器
 * 根据从数据库加载的规则进行安全控制
 */
@Slf4j
@RequiredArgsConstructor
public class DynamicSecurityFilter extends OncePerRequestFilter {

    private final SecurityRuleLoader ruleLoader;
    private final AntPathMatcher pathMatcher = new AntPathMatcher();

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        
        String path = request.getRequestURI();
        String method = request.getMethod();
        
        log.debug("处理请求: {} {}", method, path);
        
        try {
            // 检查是否是公开访问路径
            if (isPublicPath(request)) {
                log.debug("公开访问路径: {}", path);
                chain.doFilter(request, response);
                return;
            }
            
            // 检查角色权限
            if (!checkRoleAccess(request)) {
                log.debug("访问拒绝: {}", path);
                throw new AccessDeniedException("无权限访问");
            }
            
            // 继续过滤链
            chain.doFilter(request, response);
        } catch (Exception e) {
            log.error("安全过滤异常", e);
            response.sendError(HttpServletResponse.SC_FORBIDDEN, "访问被拒绝");
        }
    }

    /**
     * 检查请求是否匹配公开路径
     *
     * @param request 请求对象
     * @return 是否为公开路径
     */
    private boolean isPublicPath(HttpServletRequest request) {
        // 获取公开路径匹配器
        List<RequestMatcher> publicPathMatchers = ruleLoader.getPublicPaths();
        
        // 检查是否匹配任意一个公开路径
        for (RequestMatcher matcher : publicPathMatchers) {
            if (matcher.matches(request)) {
                return true;
            }
        }
        
        return false;
    }

    /**
     * 检查角色权限
     *
     * @param request 请求对象
     * @return 是否有权限访问
     */
    private boolean checkRoleAccess(HttpServletRequest request) {
        // 获取当前用户认证信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !authentication.isAuthenticated()) {
            return false;
        }
        
        // 获取当前路径所需角色
        List<SecurityRule> roleRules = ruleLoader.getRoleBasedRules();
        
        // 检查路径是否需要特定角色
        for (SecurityRule rule : roleRules) {
            if (rule.getPathMatcher().matches(request)) {
                // 如果规则不需要角色，则允许访问
                if (rule.getRequiredRoles() == null || rule.getRequiredRoles().isEmpty()) {
                    return true;
                }
                
                // 检查用户是否具有所需角色
                for (String role : rule.getRequiredRoles()) {
                    if (authentication.getAuthorities().stream()
                            .anyMatch(authority -> authority.getAuthority().equals("ROLE_" + role) ||
                                    authority.getAuthority().equals(role))) {
                        return true;
                    }
                }
                
                // 没有任何所需角色，拒绝访问
                return false;
            }
        }
        
        // 默认不允许访问
        return false;
    }
    
    /**
     * 根据JWT创建认证对象
     *
     * @param userDetails 用户详情
     * @return 认证对象
     */
    private Authentication createAuthentication(UserDetails userDetails) {
        return new UsernamePasswordAuthenticationToken(
                userDetails,
                null,
                userDetails.getAuthorities()
        );
    }
} 